package com.example.hookapp.base;

import android.app.Activity;
import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.blankj.utilcode.util.AppUtils;
import com.blankj.utilcode.util.ScreenUtils;

import java.util.ArrayList;
import java.util.List;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

public abstract class HookerBase {
    protected ClassLoader classLoader;
    protected boolean isHook;
    protected Context context;
    protected Activity mainActivity;

    protected int screenWidth;
    protected int screenHeight;
    private List<Activity> activities = new ArrayList<>();

    protected abstract String providePackageName();

    protected String[] providePackageNames() {
        return null;
    }

    protected abstract String provideMainActivityName();

    private void log(String s) {
        Log.e("======", "======" + s);
    }

    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        String processName = loadPackageParam.processName;
        String packageName = loadPackageParam.packageName;
        log("packageName:" + packageName + ",processName:" + processName);

        if (!processName.equals(packageName)) {
            return;
        }
        String hookName = providePackageName();
        if (hookName == null) {
            String[] strings = providePackageNames();
            if (strings == null) {
                return;
            }

            for (String s : strings) {
                if (s.equals(processName)) {
                    hookName = processName;
                    break;
                }
            }
        }

        if (hookName == null) {
            return;
        }
        if (!processName.contains(hookName)) {
            return;
        }
        if (isHook) {
            return;
        }
        isHook = true;
        log("Hook成功:" + hookName);
        String finalHookName = hookName;
        XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
            boolean isHook;

            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                Log.e("======", "beforeHookedMethod: attachBaseContext");
                if (!isHook) {
                    classLoader = loadPackageParam.classLoader;
                    isHook = true;
                    context = (Context) param.args[0];
                    onApplicationAttach(context);
                    handler = new Handler(Looper.getMainLooper());
                    toastLong("Hook成功" + finalHookName);
//                    toastLong("Hook成功" + finalHookName + "\n" + BuildConfig.BASE_URL);
                    hookStart(loadPackageParam);
                }
            }
        });

        XposedHelpers.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                super.beforeHookedMethod(param);
                Activity activity = (Activity) param.thisObject;
                String name = activity.getClass().getName();
                log("进入》》》" + name);
                sendLogMsg("进入》》》" + name);
                addActivity(activity);
                if (name.equals(provideMainActivityName())) {
                    mainActivity = activity;
                    screenWidth = ScreenUtils.getScreenWidth();
                    screenHeight = ScreenUtils.getScreenHeight();
                }
                onActivityCreate(activity, param);

            }
        });

        XposedHelpers.findAndHookMethod(Activity.class, "onDestroy", new Object[]{new XC_MethodHook() {
            protected void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {
                Activity activity = (Activity) methodHookParam.thisObject;
                removeActivity(activity);
                String name = activity.getClass().getName();
                sendLogMsg("离开" + name + "，activitys.size =" + activities.size());
                log("离开" + name + "，activitys.size =" + activities.size());
            }
        }});
    }

    private void removeActivity(Activity activity) {
        activities.remove(activity);
    }

    private void addActivity(Activity activity) {
        String name = activity.getClass().getName();
        if (!provideMainActivityName().equals(name)) {
            activities.add(activity);
        }
    }

    protected abstract void onApplicationAttach(Context context);

    protected abstract void onActivityCreate(Activity activity, XC_MethodHook.MethodHookParam param) throws Throwable;

    protected abstract void hookStart(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable;

    protected void jumpToApp() {
        AppUtils.launchApp(providePackageName());
    }

    protected Class loadClass(String className) throws ClassNotFoundException {
        return classLoader.loadClass(className);
    }

    protected void printAllView(View view, int index) {
        log(getP(index) + view);
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            index++;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                printAllView(viewGroup.getChildAt(i), index);
            }
        } else {
            if (view instanceof TextView) {
                log(getP(index) + ((TextView) view).getText().toString());
            }
        }
    }

    private String getP(int index) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < index; i++) {
            sb.append(">>>>");
        }
        return sb.toString();
    }

    protected void toastLong(String text) {
        handler.post(() -> {
            Toast.makeText(context, text, Toast.LENGTH_LONG).show();
        });
    }

    protected void toastShort(String text) {
        handler.post(() -> {
            Toast.makeText(mainActivity, text, Toast.LENGTH_SHORT).show();
        });
    }

    protected Handler handler;

    protected void findAndHookMethod(String className, String methodName, Object... objects) {
        try {
            XposedHelpers.findAndHookMethod(loadClass(className), methodName, objects);
            log("findAndHookMethod成功--》 类名：" + className + ",方法名：" + methodName);
        } catch (Exception e) {
            e.printStackTrace();
            log("异常findAndHookMethod 类名：" + className + ",方法名：" + methodName + ",异常信息：" + e.getMessage());
        }
    }

    protected void copyText(CharSequence text) {
        ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        cm.setPrimaryClip(ClipData.newPlainText("text", text));
    }

    protected Activity getCurrentActivity() {
        return activities.get(activities.size() - 1);
    }

    protected void findAndHookTextUtilsisEmpty() {
        findAndHookMethod(TextUtils.class.getName(), "isEmpty", CharSequence.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                if (param.args[0] != null && !param.args[0].equals("")) {
                    log("TextUtils.isEmpty:" + param.args[0]);
                }
            }
        });
    }

    public String getTextCenter(String text, String begin, String end) {
        try {
            int b = text.indexOf(begin) + begin.length();
            int e = text.indexOf(end, b);
            return text.substring(b, e);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return "error";
        }
    }

    protected TextView findContainsTextFromView(View view, String text) {
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                TextView textFromView = findContainsTextFromView(viewGroup.getChildAt(i), text);
                if (textFromView != null) {
                    return textFromView;
                }
            }
        } else {
            if (view instanceof TextView) {
                TextView textView = (TextView) view;
                if (textView.getText().toString().contains(text)) {
                    return textView;
                }
            }
        }
        return null;
    }

    protected TextView findEqualsTextFromView(View view, String text) {
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                TextView textFromView = findEqualsTextFromView(viewGroup.getChildAt(i), text);
                if (textFromView != null) {
                    return textFromView;
                }
            }
        } else {
            if (view instanceof TextView) {
                TextView textView = (TextView) view;
                if (textView.getText().toString().equals(text)) {
                    return textView;
                }
            }
        }
        return null;
    }

    protected void postRunnable(Runnable runnable) {
        handler.post(runnable);
    }

    protected void postDelayedRunnable(int delayed, Runnable runnable) {
        handler.postDelayed(runnable, delayed);
    }

    protected int dp2px(final float dpValue) {
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 发送日志广播
     */
    public void sendLogMsg(String msg) {
        Intent intent = new Intent(HookLogMessageFragment.class.getName());
        intent.putExtra("msg", msg);
        context.sendBroadcast(intent);
    }
}
